﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.MathLib
{
    public class PlanarSurface3d : Surface3d
    {
        public Plane Plane { get; private set; }
        public Vector3 Normal => this.Plane.Normal;
        public PlanarSurface3d(Plane plane, List<Curve3d>outerloop) 
        {
            this.Plane = plane;
            this.OuterLoop.AddRange(outerloop);
        }
        //public bool IsProjectionInFace(Vector3 point) 
        //{
        //    var proPoint = this.Plane.ProjectPoint(point);
        //    var toWorldMat = GetWorldMatrix();
        //    var fromWorldMat = toWorldMat.Clone().Invert();
        //    proPoint.ApplyMatrix4( fromWorldMat)
        //}
        private Matrix4 GetWorldMatrix() 
        {
            var toWorldMat = ThreadContext.GetCurrThreadContext().Matrix4Ctx._m1;
            var loopPs = this.OuterLoop[0].GetPoints();
            var origin = loopPs[0];
            var xAxis = loopPs[1].Clone().Sub(origin).Normalize();
            var yAxis = Normal.Clone().Cross(xAxis).Normalize();
            toWorldMat.Identity().MakeBasis(xAxis, yAxis, Normal);
            toWorldMat.SetPosition(origin);
            return toWorldMat;
        }
        public override Tuple<double[], int[]> Trianglate()
        {
            var toWorldMat = GetWorldMatrix();
            var fromWorldMat = toWorldMat.Clone().Invert();
            var allPoints = new ListEx<Vector3>();
            var outerLoop = new ListEx<Vector2>();
            var innerLoops=new ListEx< ListEx<Vector2>>();
            var indices=new ListEx<int>();
            for (int i = 0; i <  this.OuterLoop.Count; i++)
            {
                var ps = this.OuterLoop[i].GetPoints();
                var points = ps.Select(p => p.Clone().ApplyMatrix4(fromWorldMat)).ToList();
                for (int li = 0; li < points.Count-1; li++)
                {
                    var p = new Vector2(points[li].X, points[li].Y);
                    allPoints.Add(ps[li]);
                    outerLoop.Add(p);
                }
            }
            for (int i = 0; i < this.InnerLoops.Count; i++)
            {
                var loop3 = this.InnerLoops[i];
                var loop2 = new ListEx<Vector2>();
                innerLoops.Add(loop2);
                for (int j = 0; j < loop3.Count; j++)
                {
                    var ps = loop3[j].GetPoints();
                    var points = ps.Select(p => p.Clone().ApplyMatrix4(fromWorldMat)).ToList();
                    for (int li = 0; li < points.Count-1; li++)
                    {
                        var p = new Vector2(points[li].X, points[li].Y);
                        loop2.Add(p);
                        allPoints.Add(ps[li]);
                    }
                }
            }
            TriangulateOutput output = new TriangulateOutput();
            var triIdxs= ShapeUtils.triangulateShape(outerLoop, innerLoops, output);
            for (int i = 0; i < triIdxs.Length; i++)
            {
                var faceIdx = triIdxs[i];
                indices.Push(faceIdx[0], faceIdx[1], faceIdx[2]);
            }

            var posArr = Utils.GenerateVertexArr(allPoints);
            return new Tuple<double[], int[]>(posArr, indices.ToArray());
        }

        public override void Translate(Vector3 offset)
        {
            this.Plane.Translate(offset);
            foreach (var loop in this.OuterLoop)
            {
                loop.Translate(offset);
            }
            foreach (var loops in this.InnerLoops)
            {
                foreach (var loop in loops)
                {
                    loop.Translate(offset);
                }
            }
        }

        public override void RotateRoundAxis(Vector3 origin, Vector3 axis, double angle)
        {
            var matrix = new Matrix4().MakeRotationAxis(axis, angle);
            this.Plane.ApplyMatrix4(matrix);

            foreach (var loop in this.OuterLoop)
            {
                loop.RotateRoundAxis(origin, axis, angle);
            }
            foreach (var loops in this.InnerLoops)
            {
                foreach (var loop in loops)
                {
                    loop.RotateRoundAxis(origin, axis, angle);
                }
            }
        }
    }
}